import java.awt.Graphics2D;
import java.awt.geom.CubicCurve2D;
import java.awt.geom.Point2D;

public class LoopArrow extends Arrow implements Paintable {
	private final CubicCurve2D baseCurve = new CubicCurve2D.Double();
	private final ArrowHead arrowHead;
	private final Point2D ctrlPoint1 = new Point2D.Double();
	private final Point2D ctrlPoint2 = new Point2D.Double();

	public LoopArrow(Quiver quiver, Node node) {
		super(quiver, node, node);
		arrowHead = new ArrowHead(config, this);
		setName();
	}
	
	@Override
	public void update() {
		double angle = fromNode.calculateAngle(this);
		fromAngle = angle  + config.arrowLoopAngle - Math.PI/2;
		toAngle = angle - config.arrowLoopAngle - Math.PI/2;
				
		fromPoint.setLocation(fromNode.getPoint());
		toPoint.setLocation(toNode.getPoint());
		// adjust for radius of nodes
		fromPoint.setLocation(fromPoint.getX() + config.visibleNodeRadius
				* Math.cos(fromAngle), fromPoint.getY()
				+ config.visibleNodeRadius * Math.sin(fromAngle));
		toPoint.setLocation(toPoint.getX() - config.visibleNodeRadius
				* Math.cos(toAngle), toPoint.getY() - config.visibleNodeRadius
				* Math.sin(toAngle));

		ctrlPoint1.setLocation(fromPoint.getX() + config.arrowLoopSize
				* Math.cos(fromAngle), fromPoint.getY() + config.arrowLoopSize
				* Math.sin(fromAngle));

		ctrlPoint2.setLocation(toPoint.getX() + config.arrowLoopSize
				* Math.cos(toAngle + Math.PI), toPoint.getY() + config.arrowLoopSize
				* Math.sin(toAngle + Math.PI));

		baseCurve.setCurve(fromPoint, ctrlPoint1, ctrlPoint2, toPoint);
		arrowHead.update();
	}

	@Override
	public Point2D getMidpoint() {
		// based on formula from
		// http://en.wikipedia.org/wiki/Bézier_curve#Cubic_B.C3.A9zier_curves
		double x = 0.25 * fromPoint.getX() + 0.375
				* (ctrlPoint1.getX() + ctrlPoint2.getX());
		double y = 0.25 * fromPoint.getY() + 0.375
				* (ctrlPoint1.getY() + ctrlPoint2.getY());
		return new Point2D.Double(x, y);
	}

	@Override
	public void paint(Graphics2D g) {
		g.setPaint(config.arrowColor);
		arrowHead.paint(g);
		g.draw(baseCurve);
		if (config.showLabels) {
			paintLabel(g);
		}
	}

	@Override
	public boolean isClose(Point2D point) {
		Point2D midpoint = getMidpoint();
		double centerX = 0.5 * (fromPoint.getX() + midpoint.getX());
		double centerY = 0.5 * (fromPoint.getY() + midpoint.getY());
		double radius = midpoint.distance(centerX, centerY);
		double dist = point.distance(centerX, centerY);
		return dist > radius - config.arrowWidth
				&& dist < radius + config.arrowWidth;
	}
}
